#pragma once
#include <iostream>
#include <string>
#include <vector>
#include <memory>
#include <sstream>
#include <unordered_map>
#include <functional>
#include <fstream>
#include "Log.hpp"

static const std::string sep = "\r\n";
static const std::string header_sep = ": ";
static const std::string wwwroot = "wwwroot";
static const std::string homepage = "index.html";
static const std::string httpversion = "HTTP/1.0";
static const std::string space = " ";
static const std::string filesuffixsep = ".";
static const std::string args_sep = "?";

class HttpResponse;
class HttpRequest;

using func_t = std::function<std::shared_ptr<HttpResponse> (std::shared_ptr<HttpRequest>)>;

class HttpRequest
{
private:
    std::string GetOneLine(std::string &reqstr)
    {
        if (reqstr.empty())
            return reqstr;
        auto pos = reqstr.find(sep);
        if (pos == std::string::npos)
            return std::string();

        std::string line = reqstr.substr(0, pos);
        reqstr.erase(0, pos + sep.size());
        return line.empty() ? sep : line;
    }

    bool ParseHeaderHelper(const std::string &line, std::string *k, std::string *v)
    {
        auto pos = line.find(header_sep);
        if (pos == std::string::npos)
            return false;

        *k = line.substr(0, pos);
        *v = line.substr(pos + header_sep.size());
        return true;
    }

public:
    HttpRequest() : _blank_line(sep), _path(wwwroot)
    {
    }

    void Serialize()
    {
    }

    void Derialize(std::string &reqstr)
    {
        _req_line = GetOneLine(reqstr);

        while (true)
        {
            std::string line = GetOneLine(reqstr);
            if (line.empty())
            {
                break;
            }
            else if (line == sep)
            {
                _req_text = reqstr;
                break;
            }
            else
            {
                _req_header.emplace_back(line);
            }
        }
        ParseReqLine();
        ParseHeader();
    }

    void Print()
    {
        std::cout << "====" << _req_line << std::endl;
        for (auto &header : _req_header)
        {
            std::cout << "*****" << header << std::endl;
        }
        std::cout << _blank_line;
        std::cout << "====" << _req_text << std::endl;

        std::cout << "-----------------------------------" << std::endl;
        std::cout << "method ####" << _method << std::endl;
        std::cout << "url ####" << _url << std::endl;
        std::cout << "path ####" << _path << std::endl;
        std::cout << "httpversion ####" << _version << std::endl;

        for (auto &header : _headers)
        {
            std::cout << "@@@@@@" << header.first << " - " << header.second << std::endl;
        }
    }

    bool ParseReqLine()
    {
        if (_req_line.empty())
        {
            LOG(INFO, "falsefalsefalsefalsefalse\n");
            return false;
        }
        std::stringstream ss(_req_line);
        ss >> _method >> _url >> _version;

        auto pos = _url.find(args_sep);
        if (pos != std::string::npos)
        {
            LOG(INFO, "-------change begin------: url %s\n", _url.c_str());
            _args = _url.substr(pos + args_sep.size());
            _url.resize(pos);
            LOG(INFO, " change done: url %s, _args %s\n", _url.c_str(), _args.c_str());
        }

        _path += _url;

        LOG(DEBUG, "url: %s\n", _url.c_str());
        if (_path[_path.size() - 1] == '/')
        {
            _path += homepage;
        }

        pos = _path.rfind(filesuffixsep);
        if (pos == std::string::npos)
        {
            _suffix = ".unknown";
        }
        else
        {
            _suffix = _path.substr(pos);
        }

        LOG(INFO, "client want get %s, _suffix: %s\n", _path.c_str(), _suffix.c_str());
        // fflush(stdout);
        return true;
    }

    bool ParseHeader()
    {
        for (auto &header : _req_header)
        {
            std::string k, v;
            if (ParseHeaderHelper(header, &k, &v))
            {
                _headers.insert(std::make_pair(k, v));
                //_headers.insert(std::pair<std::string,std::string>(k,v));
                //_headers.insert({k,v});
            }
        }
        return true;
    }
    std::string Path()
    {
        return _path;
    }
    bool IsExec()
    {
        return !_args.empty() || _req_text.empty();
    }
    std::string Suffix()
    {
        return _suffix;
    }
    std::string Args()
    {
        return _args;
    }

    std::string Text()
    {
        return _req_text;
    }

    std::string Method()
    {
        return _method;
    }

    ~HttpRequest()
    {
    }

private:
    // 原始协议内容
    std::string _req_line;
    std::vector<std::string> _req_header;
    std::string _blank_line;
    std::string _req_text;

    // 期望解析内容
    std::string _method;
    std::string _url;
    std::string _args;
    std::string _path;
    std::string _suffix;
    std::string _version;

    std::unordered_map<std::string, std::string> _headers;
};

class HttpResponse
{
public:
    HttpResponse() : _version(httpversion), _blank_line(sep)
    {
    }
    void AddStatusLine(int code, const std::string &desc)
    {
        _code = code;
        _desc = desc; // TODO
    }

    void AddHeader(const std::string &k, const std::string &v)
    {

        LOG(DEBUG, "AddHeader :%s->%s\n", k.c_str(), v.c_str());
        _headers[k] = v; // insert
    }
    void AddText(const std::string &text)
    {
        _resp_text = text;
    }
    std::string Serialize()
    {
        std::string _status_line = _version + space + std::to_string(_code) + space + _desc + sep;
        for (auto &header : _headers)
        {
            _resp_header.emplace_back(header.first + header_sep + header.second + sep);
        }

        std::string respstr = _status_line;
        for (auto &header : _resp_header)
        {
            respstr += header;
        }
        respstr += _blank_line;
        respstr += _resp_text;
        return respstr;
    }

    ~HttpResponse() {}

private:
    // 构建应答的必要字段
    std::string _version;
    int _code;
    std::string _desc;
    std::unordered_map<std::string, std::string> _headers;

    std::string _req_line;
    std::vector<std::string> _resp_header;
    std::string _blank_line;
    std::string _resp_text;
};

class Factory
{
public:
    static std::shared_ptr<HttpRequest> BuildHttpRequest()
    {
        return std::make_shared<HttpRequest>();
    }

    static std::shared_ptr<HttpResponse> BuildHttpResponse()
    {
        return std::make_shared<HttpResponse>();
    }
};

class HttpServer
{
public:
    HttpServer()
    {
        _mime_type.insert(std::make_pair(".html", "text/html"));
        _mime_type.insert(std::make_pair(".css", "text/css"));
        _mime_type.insert(std::make_pair(".js", "application/x-javascript"));
        _mime_type.insert(std::make_pair(".png", "image/png"));
        _mime_type.insert(std::make_pair(".jpg", "image/jpeg"));
        _mime_type.insert(std::make_pair(".unknown", "text/html"));

        _code_to_desc.insert(std::make_pair(100, "Continue"));
        _code_to_desc.insert(std::make_pair(200, "OK"));
        _code_to_desc.insert(std::make_pair(301, "Moved Permanently"));
        _code_to_desc.insert(std::make_pair(302, "Found"));
        _code_to_desc.insert(std::make_pair(404, "Not Found"));
        _code_to_desc.insert(std::make_pair(500, "Internal Server Error"));
    }

    void AddHandler(const std::string functionname, func_t f)
    {
        std::string key = wwwroot + functionname; // wwwroot/login
        _funcs[key] = f;
    }

    std::string ReadFileContent(const std::string &path, int *size)
    {
        // 要按照二进制的方式打开
        std::ifstream in(path, std::ios::binary);
        if (!in.is_open())
        {
            return std::string();
        }
        in.seekg(0, in.end);
        int filesize = in.tellg();
        in.seekg(0, in.beg);

        std::string content;
        content.resize(filesize);
        in.read((char *)content.c_str(), filesize);
        in.close();
        *size = filesize;
        return content;
    }

    std::string HandlerHttpRequest(std::string req)
    {
// #define TEST
#ifdef TEST
        std::cout << "---------------------------------------" << std::endl;
        std::cout << req;

        std::string response = "HTTP/1.0 200 OK\r\n"; // 404 NOT Found
        response += "\r\n";
        response += "<html><body><h1>hello world, hello gm!</h1></body></html>";

        return response;
#else
        auto request = Factory::BuildHttpRequest(); // BuildHttprequest() static修饰了，不用this指针就能调用
        request->Derialize(req);                    // 拿到请求反序列化

        // std::string newurl = "http://139.159.246.8:8888/image/huosang.png";
        // int code = 0;
        // if (request->Path() == "wwwroot/redir")
        // {
        //     code = 301;
        //     response->AddStatusLine(code, _code_to_desc[code]);
        //     response->AddHeader("Location", newurl);
        // }

        if (request->IsExec())
        {
            auto response = _funcs[request->Path()](request);
            return response->Serialize();
        }
        else
        {
            auto response = Factory::BuildHttpResponse();
            int code = 200;
            int contentsize = 0;
            std::string text = ReadFileContent(request->Path(), &contentsize);
            if (text.empty())
            {
                code = 404;
                response->AddStatusLine(code, _code_to_desc[code]);
                std::string text404 = ReadFileContent("wwwroot/404.html", &contentsize);
                response->AddHeader("Content-Length", std::to_string(contentsize));
                response->AddHeader("Content-Type", _mime_type[".html"]);
                response->AddText(text404);
            }

            else
            {

                response->AddStatusLine(code, _code_to_desc[code]);
                response->AddHeader("Content-Length", std::to_string(contentsize));
                response->AddText(text);
                std::string suffix = request->Suffix();
                response->AddHeader("Content-Type", _mime_type[suffix]);
            }
            return response->Serialize();
        }

        // response->AdeHeader("Content-Length", std::to_string(contentsize));
        // response->AdeHeader("Content-Type", "text/html");

#endif
    }
    ~HttpServer() {}

private:
    std::unordered_map<std::string, std::string> _mime_type;
    std::unordered_map<int, std::string> _code_to_desc;
    std::unordered_map<std::string, func_t> _funcs;
};